package psnl.bingo.bgtool.model.util;

import cn.hutool.core.io.FileUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import lombok.extern.slf4j.Slf4j;
import psnl.bingo.bgtool.model.constant.ConstCTX;
import psnl.bingo.bgtool.model.dto.*;

import java.io.*;
import java.util.*;

@Slf4j
public class TDTool {
    private static String spt = ";";
    private static long sizeMB = 1024 * 1024;

    private static final String COLUMN_SPLIT_CHAR = "C.S.P";
    private static int fileNameForSizeIndex = 0;
    private static long fileNameForSizeSize = 0L;

    /** 过滤逻辑 数据行长度信息逻辑条件 */
    private static LinkedHashMap<String, String> filterRowLengthLogicType = new LinkedHashMap<>();
    /** 过滤逻辑 数据行内容信息逻辑条件 */
    private static LinkedHashMap<String, String> filterRowContentLogicType = new LinkedHashMap<>();
    /** 过滤逻辑 数据字段值逻辑条件 */
    private static LinkedHashMap<String, String> filterColumnLogicType = new LinkedHashMap<>();

    static {
        filterRowLengthLogicType.put("=", "logicEq");
        filterRowLengthLogicType.put(">", "logicBig");
        filterRowLengthLogicType.put(">=", "logicBigEq");
        filterRowLengthLogicType.put("<", "logicLit");
        filterRowLengthLogicType.put("<=", "logicLitEq");

        filterRowContentLogicType.put("contain", "logicContain");
        filterRowContentLogicType.put("unContain", "logicUnContain");
        filterRowContentLogicType.put("startsWith", "logicStartWith");
        filterRowContentLogicType.put("endsWith", "logicEndsWith");

        filterColumnLogicType.put("=", "logicEq");
        filterColumnLogicType.put(">", "logicBig");
        filterColumnLogicType.put(">=", "logicBigEq");
        filterColumnLogicType.put("<", "logicLit");
        filterColumnLogicType.put("<=", "logicLitEq");
        filterColumnLogicType.put("startsWith", "logicStartWith");
        filterColumnLogicType.put("endsWith", "logicEndsWith");
        filterColumnLogicType.put("contain", "logicContain");
        filterColumnLogicType.put("unContain", "logicUnContain");
    }

    public static LinkedHashMap<String, String> getFilterRowLengthLogicType() {
        return filterRowLengthLogicType;
    }

    public static LinkedHashMap<String, String> getFilterRowContentLogicType() {
        return filterRowContentLogicType;
    }

    public static LinkedHashMap<String, String> getFilterColumnLogicType() {
        return filterColumnLogicType;
    }

    public static void handleData(TDParam conf) throws Exception {
        TDParam.progress = "正在执行。。。";
        Thread.sleep(3000);

        fileNameForSizeIndex = 0;
        fileNameForSizeSize = 0;


        List<String> caseLines = null;
        if(null != conf.caseFile) {
            if(conf.caseFile.length() > 10 * sizeMB) {
                TDParam.progress = "条件文件过大(不能超过 10MB)";
                return;
            }

            caseLines = FileUtil.readLines(conf.caseFile, ConstCTX.charsetUTF8);

        }

        String[] items;
        // 执行条件过滤操作
        if(conf.filterConf != null)
        {
            if(null != caseLines && conf.filterConf.caseFileMatch != null) {
                Set<String> ftCase = new HashSet<>();
                String item;
                for (String line : caseLines) {
                    items = line.split(conf.caseSplit);
                    item = "";
                    for (Integer index : conf.filterConf.caseFileMatch.values()) {
                        item += items[index];
                    }
                    ftCase.add(item);
                }

                conf.filterConf.caseFT = ftCase;
            }
        }

        // 执行数据转换操作
        if(conf.transConf != null)
        {
            if(null != caseLines && null != conf.transConf.caseFileMatch) {
                Map<String, String> tsCase = new HashMap<>();
                String key, val;

                for (String line : caseLines) {
                    items = line.split(conf.caseSplit);

                    key = ""; // 条件文件数据记录的数据项组成的key
                    for (Integer index : conf.transConf.caseFileMatch.values()) {
                        key += items[index];
                    }

                    val = ""; // 条件文件数据记录的数据项组成的需要被替换成的值
                    for (Integer index : conf.transConf.caseValueIndexes) {
                        val += items[index];
                    }
                    tsCase.put(key, val);
                }

                conf.transConf.caseColValueMap = tsCase;
            }
        }

        String lineStr, tempStr, fileName;
        long lineNum=0, lineNumTotal=0, saveNum=0, saveNumTmp=0, saveNumTotal=0;
        String[] lineArray;
        LinkedHashMap<String, List<String>> fileName2dataList = new LinkedHashMap<>();
        int fileIndex = 1, fileTotal = conf.sourceFiles.size();
        for (File dataFile : conf.sourceFiles) {
            lineNum = 0;
            saveNum=0;
            fileName = dataFile.getName();
            BufferedInputStream bis = null;
            BufferedReader in = null;

            try {
                bis = new BufferedInputStream(new FileInputStream(dataFile));
                in = new BufferedReader(new InputStreamReader(bis, "utf-8"), 10 * 1024 * 1024);

                while(in.ready()) {
                    lineStr = in.readLine();

                    if(lineStr == null) break;

                    lineNum++;
                    lineNumTotal++;

                    if(lineNum % 10000 == 0) {
                        TDParam.progress = "文件进度：" + fileIndex + "/" + fileTotal + "， 当前文件=" + fileName + "{读取行数="+lineNum + ", 写入行数=" + saveNum + "}, "+
                                "统计{读取行数=" + lineNumTotal + ", 写入行数=" + saveNumTotal + "}";
                    }

                    lineArray = lineStr.split(conf.sourceSplit);
                    if(conf.filterConf != null && !conf.filterConf.saveLine(lineStr, lineArray)) {
                        // 不符合过滤条件
                        continue;
                    }

                    if(conf.transConf != null) {
                        lineStr = transLine(conf, lineStr, lineArray);
                    }

                    if(lineStr.contains(COLUMN_SPLIT_CHAR)) {
                        tempStr = lineStr;
                        while(tempStr.contains(COLUMN_SPLIT_CHAR) && tempStr.length() > COLUMN_SPLIT_CHAR.length()) {
                            lineStr = tempStr.substring(0, tempStr.indexOf(COLUMN_SPLIT_CHAR));

                            saveNumTmp = addData2Queue(conf, fileName2dataList, lineStr, fileName);
                            saveNum += saveNumTmp;
                            saveNumTotal += saveNumTmp;

                            tempStr = tempStr.substring(tempStr.indexOf(COLUMN_SPLIT_CHAR)+COLUMN_SPLIT_CHAR.length());
                        }
                    }else {
                        saveNumTmp = addData2Queue(conf, fileName2dataList, lineStr, fileName);
                        saveNum += saveNumTmp;
                        saveNumTotal += saveNumTmp;
                    }
                }

                List<String> otherData;
                for (String fName : fileName2dataList.keySet()) {
                    otherData = fileName2dataList.get(fName);
                    if(otherData.size() > 0) {
                        File outputFile = new File(conf.outputPath + "/" + fName);
                        if(!outputFile.exists()) {
                            outputFile.createNewFile();
                        }
                        FileUtil.writeLines(otherData, outputFile, ConstCTX.charsetUTF8, true);

                        saveNum += otherData.size();
                        saveNumTotal += otherData.size();

                        otherData.clear();
                    }
                }


            }catch (Exception e) {
                TDParam.progress = "数据处理异常！文件进度：" + fileIndex + "/" + fileTotal + "， 当前文件=" + fileName + "{读取行数="+lineNum + ", 写入行数=" + saveNum + "}, "+
                        "统计{读取行数=" + lineNumTotal + ", 写入行数=" + saveNumTotal + "}";
                e.printStackTrace();
            }finally {
                try {
                    if(bis != null) bis.close();
                    if(in != null) in.close();
                }catch (Exception e) {
                    e.printStackTrace();
                }
            }

            if(TDParam.progress != null && TDParam.progress.startsWith("数据处理异常")) {
                break;
            }
        }

        TDParam.isDoing = false;
        TDParam.progress = "处理完成。读取总行数：" + lineNumTotal + "， 写入总行数：" + saveNumTotal;
    }

    private static String transLine(TDParam conf, String lineStr, String[] lineArray) {
        String result = lineStr;

        TDTrans transLogic = conf.transConf;

        if(transLogic.removeIndexes != null) {
            // 移除指定列

            String[] transData = new String[lineArray.length-transLogic.removeIndexes.size()];

            int transIndex = 0;

            for (int i = 0; i < lineArray.length; i++) {
                if(transLogic.removeIndexes.contains(i)) {
                    continue;
                }else {
                    transData[transIndex++] = lineArray[i];
                }
            }

            result = String.join(conf.sourceSplit, transData);
        }else if(transLogic.splitColIndex != null) {
            // 拆分指定列

            StringBuilder sb = new StringBuilder();
            String preStr = "", sufStr = "", spChar = "", tempLine="";
            for (int i = 0; i < lineArray.length; i++) {
                if(i<transLogic.splitColIndex) {
                    preStr += lineArray[i] + conf.sourceSplit;
                }else if(i>transLogic.splitColIndex) {
                    sufStr += conf.sourceSplit + lineArray[i];
                }else {
                    spChar = lineArray[i];
                }
            }

            for (String value : spChar.split(transLogic.splitColSplit)) {
                tempLine = preStr + value + sufStr;
                if(tempLine.endsWith(conf.sourceSplit)) {
                    tempLine = tempLine.substring(0, tempLine.length()-conf.sourceSplit.length());
                }
                sb.append(tempLine+COLUMN_SPLIT_CHAR);
            }

            result = sb.toString();
        }else if(transLogic.setColIndex != null) {
            // 直接指定列值

            lineArray[transLogic.setColIndex] = transLogic.setColValue;
            result = String.join(conf.sourceSplit, lineArray);
        }else if(transLogic.caseFileMatch != null) {
            // 使用条件文件映射指定

            String replaceKey = "";
            for (Integer key : transLogic.caseFileMatch.keySet()) {
                replaceKey += lineArray[key];
            }

            if(transLogic.caseColValueMap.keySet().contains(replaceKey)) {
                lineArray[transLogic.caseColIndex] = transLogic.caseColValueMap.get(replaceKey);
                result = String.join(conf.sourceSplit, lineArray);
            }
        }

        return result;
    }

    private static long addData2Queue(TDParam conf, LinkedHashMap<String, List<String>> fileName2dataList, String lineStr,
                                      String orgFileName) throws IOException {
        int savedNum = 0;

        String fileName = computeFileName(conf, lineStr, orgFileName);

        List<String> dataList = fileName2dataList.get(fileName);
        if(dataList == null) {
            dataList = new ArrayList<>();
            fileName2dataList.put(fileName, dataList);
        }

        if(!conf.sourceSplit.equals(conf.outputSplit)) {
            lineStr = lineStr.replace(conf.sourceSplit, conf.outputSplit);
        }

        dataList.add(lineStr);

        if(dataList.size() > 70000) {
            // 数据写入到文件
            File outputFile = new File(conf.outputPath + "/" + fileName);
            if(!outputFile.exists()) {
                outputFile.createNewFile();
            }

            FileUtil.writeLines(dataList, outputFile, ConstCTX.charsetUTF8, true);
            savedNum = dataList.size();
            dataList.clear();

            if(conf.expSize != null) {
                fileNameForSizeSize = outputFile.length() / sizeMB;
            }
        }

        return savedNum;
    }

    private static String computeFileName(TDParam conf, String lineStr, String orgFileName) {
        String fileName = orgFileName;

        if(conf.expNum != null) {
            if(conf.expNum > 1) {
                fileName = "part-" + String.format("%03d", ConstCTX.RDObj.nextInt(conf.expNum));
            }else {
                fileName = "part-000";
            }
        }else if(conf.expSize != null) {
            if(fileNameForSizeSize + 5 > conf.expSize) {
                fileNameForSizeIndex ++;
                fileNameForSizeSize = 0;
            }
            fileName = "part-" + String.format("%03d", fileNameForSizeIndex);
        }else if(conf.expColIndex != null) {
            String[] dataArray = lineStr.split(spt);

            fileName = "";
            for (int index : conf.expColIndex) {
                fileName += "_" + dataArray[index];
            }
            fileName = fileName.substring(1);
        }

        return fileName;
    }

    public static TDParam checkParam(JSONObject param) {
        TDParam.isDoing = true;
        TDParam.progress = "";

        TDParam obj = new TDParam();

        File sourceFile = new File(param.getStr("sourcePath"));
        if(sourceFile.exists()) {
            if(sourceFile.isFile()) {
                obj.sourceFiles.add(sourceFile);
            }else {
                for (File file : sourceFile.listFiles()) {
                    obj.sourceFiles.add(file);
                }
            }
            obj.sourceSplit = param.getStr("sourceSplit");

            if(param.containsKey("casePath")) {
                File caseFile = new File(param.getStr("casePath"));
                if(caseFile.exists()) {
                    obj.caseFile = caseFile;
                    obj.caseSplit = param.getStr("caseSplit");
                }else {
                    obj.err = "条件文件不存在";
                    return obj;
                }
            }

            JSONObject expConf = param.getJSONObject("exportConf");
            File outFile = new File(expConf.getStr("output"));
            if(outFile.exists() && outFile.isDirectory()) {
                obj.outputPath = outFile.getAbsolutePath();
                obj.outputSplit = expConf.containsKey("dataSplit")?expConf.getStr("dataSplit"):spt;

                String paramErr = "";

                if("true".equals(expConf.getStr("expByName"))) {
                    obj.expByName = true;
                }
                else
                {
                    try {
                        paramErr = "数据导出设置-基于文件个数参数设置有误";
                        if (expConf.containsKey("expByFileNum")) {
                            obj.expNum = Integer.parseInt(expConf.getStr("expByFileNum"));
                        }

                        paramErr = "数据导出设置-基于文件大小参数设置有误";
                        if (expConf.containsKey("expByFileSize")) {
                            obj.expSize = Integer.parseInt(expConf.getStr("expByFileSize"));
                        }

                        paramErr = "数据导出设置-基于数据列值参数设置有误";
                        if (expConf.containsKey("expByCol")) {
                            String colIndexStr = expConf.getStr("expByCol");
                            List<Integer> expColIndex = new ArrayList<>();
                            if (colIndexStr.contains(spt)) {
                                for (String index : colIndexStr.split(spt)) {
                                    expColIndex.add(Integer.parseInt(index));
                                }
                            } else {
                                expColIndex.add(Integer.parseInt(colIndexStr));
                            }
                        }

                        paramErr = "";
                    }catch (Exception e) {
                        e.printStackTrace();
                    }

                    if(paramErr.length()!=0) {
                        obj.err = paramErr;
                        return obj;
                    }
                }

                JSONObject FP;

                try {
                    if (param.containsKey("filterConf")) {
                        FP = param.getJSONObject("filterConf");
                        TDFilter FC = new TDFilter();
                        obj.filterConf = FC;

                        paramErr = "条件过滤-行数据长度参数设置有误";
                        JSONObject fObj = FP.getJSONObject("lineLength");
                        if (fObj != null) {
                            FC.useLineLength = Boolean.parseBoolean(fObj.getStr("use"));
                            FC.lineLengthOpt = fObj.getStr("opt");
                            FC.lineLength = Integer.parseInt(fObj.getStr("value"));
                        }

                        paramErr = "条件过滤-行数据内容参数设置有误";
                        fObj = FP.getJSONObject("lineContent");
                        if (null != fObj) {
                            FC.useLineContent = Boolean.parseBoolean(fObj.getStr("use"));
                            FC.lineContentOpt = fObj.getStr("opt");
                            FC.lineContent = fObj.getStr("value");
                        }

                        paramErr = "条件过滤-指定列值去重参数设置有误";
                        if (FP.containsKey("repeatRemove")) {
                            FC.repeatIndex = new ArrayList<>();
                            for (String index : FP.getStr("repeatRemove").split(spt)) {
                                FC.repeatIndex.add(Integer.parseInt(index));
                            }
                        }

                        paramErr = "条件过滤-使用条件文件参数设置有误，分隔符不合法";
                        fObj = FP.getJSONObject("caseFileFilter");
                        if (null != fObj) {
                            FC.useCaseFile = Boolean.parseBoolean(fObj.getStr("use"));
                            String matchStr = fObj.getStr("match");
                            FC.caseFileMatch = new HashMap<>();
                            for (String mc : matchStr.split(spt)) {
                                FC.caseFileMatch.put(Integer.parseInt(mc.split("-")[0]), Integer.parseInt(mc.split("-")[1]));
                            }
                        }

                        paramErr = "条件过滤-列字符过滤参数设置有误";
                        JSONArray colCase = FP.getJSONArray("colCaseList");
                        if (!colCase.isEmpty()) {
                            FC.colCases = new ArrayList<>();
                            for (int i = 0; i < colCase.size(); i++) {
                                fObj = colCase.getJSONObject(i);
                                FC.colCases.add(new TDCol(Integer.parseInt(fObj.getStr("index")),
                                        fObj.getStr("opt"), fObj.getStr("value")));
                            }
                        }
                    }

                    if (param.containsKey("transConf")) {
                        FP = param.getJSONObject("transConf");
                        TDTrans TC = new TDTrans();
                        obj.transConf = TC;

                        if (FP.containsKey("removeIndex")) {
                            paramErr = "列值转换-删除指定列参数有误，分隔符不合法";
                            TC.removeIndexes = new ArrayList<>();
                            for (String index : FP.getStr("removeIndex").split(spt)) {
                                TC.removeIndexes.add(Integer.parseInt(index));
                            }
                        } else if (FP.containsKey("colSetVal")) {
                            paramErr = "列值转换-设置指定列值的参数有误";
                            // index@value
                            String colSet = FP.getStr("colSetVal");
                            TC.setColIndex = Integer.parseInt(colSet.split("@")[0]);
                            TC.setColValue = colSet.split("@")[1];
                        } else if (FP.containsKey("colSplitLine")) {
                            paramErr = "列值转换-列值拆分到行参数有误";
                            // index@splitChart
                            String colSplit = FP.getStr("colSplitLine");
                            TC.splitColIndex = Integer.parseInt(colSplit.split("@")[0]);
                            TC.splitColSplit = colSplit.split("@")[1];
                        } else if (FP.containsKey("transByCase")) {
                            paramErr = "列值转换-使用条件文件进行转换参数有误";
                            JSONObject byCase = FP.getJSONObject("transByCase");
                            // indexA1-indexB1;indexA2-indexB2
                            String caseMatchStr = byCase.getStr("indexMatch");
                            TC.caseFileMatch = new HashMap<>();
                            for (String str : caseMatchStr.split(spt)) {
                                TC.caseFileMatch.put(Integer.parseInt(str.split("-")[0]), Integer.parseInt(str.split("-")[1]));
                            }
                            TC.caseColIndex = Integer.parseInt(byCase.getStr("transIndex"));
                            TC.caseValueIndexes = new ArrayList<>();
                            for (String index : byCase.getStr("caseIndex").split(spt)) {
                                TC.caseValueIndexes.add(Integer.parseInt(index));
                            }
                        }
                    }

                    paramErr = "";
                }catch (Exception e) {
                    e.printStackTrace();
                }

                if(paramErr.length() != 1) {
                    obj.err = paramErr;
                    return obj;
                }
            }else {
                obj.err = "导出数据目录不存在";
                return obj;
            }

        }else {
            obj.err = "数据文件不存在";
        }

        return obj;
    }
}
